#include <iostream>
// 探寻C++类的内存模型

#if 0
    // 空类：1个字节（在C语言里，空结构体是0个）
    // 原因：对于C语言来讲，结构体就是按照某种对齐方式将多个不同变量排在一起的复合数据类型，它没有对象的概念，所以它也没有构造和析构函数这些说法
    // 但是C++里面，哪怕是个空类，也有构造函数，所有的类，编译器都会为它自动生成几个缺省的函数（构造、析构、重载、拷贝），所以C++的复合数据类型等于多了一个构造析构对象的概念，它把它看作是一个对象，不光是一块空内存
    // 如果对象不占内存，那构建对象时就会出现多个对象在同一个位置的情况
    // C++里规定，不同的对象应当具有不同的内存地址，所以空类也要占一个字节
    class Test
    {};

    int main()
    {
        std::cout<<sizeof(Test)<<std::endl;
        return 0;
    }
#endif

#if 0
    // C++的类和C语言的结构体一样，依旧遵循字节对齐原则
    class Test
    {
    public:
        int a;
        char b;
    };

    int main()
    {
        std::cout<<sizeof(Test)<<std::endl;
        return 0;
    }
#endif

#if 0
    // 函数不占内存
    // 对象构建在堆上，那变量就在堆上，对象在栈上，变量就在栈上
    class Test
    {
    public:
        void show()
        {}
    public:
        int a;
        char b;
    };

    int main()
    {
        std::cout<<sizeof(Test)<<std::endl;
        return 0;
    }
#endif

#if 0
    // 静态变量不占空间
    // 如果它占对象空间，那这个静态变量会属于每一个对象
    class Test
    {
    public:
        void show()
        {}
    public:
        int a;
        char b;
        static int c;
    };

    int main()
    {
        std::cout<<sizeof(Test)<<std::endl;
        return 0;
    }
#endif

#if 0
    class Test
    {
    public:
        // 函数不占空间
        // 虚函数占一个虚函数指针的空间
        // 想用多态，就得付出相应的代价，这里就是八个字节的虚函数指针
        virtual void show()
        {}
    public:
        int a;
        char b;
    };

    int main()
    {
        std::cout<<sizeof(Test)<<std::endl;
        return 0;
    }
#endif

#if 0
    class Test
    {
    public:
        virtual void show()
        {}
    public:
        int a;
        char b;
    };

    int main()
    {
        Test t;
        // 指针在内存里放最前面
        printf("%p %p %p\n", &t, &t.a, &t.b);   
        return 0;
    }
#endif

#if 0
    // 引入继承
    class Test
    {
    public:
        void show()
        {}
    public:
        int a;
        char b;
    };

    // 如果空类继承空类，那还是占一个字节
    class Derived:public Test
    {
    public:
        // int c;
        // 基类和子类之间是不能够拼接空间的，内存分类排布
        char c;
    };

    int main()
    {
        Derived t;
        std::cout<<sizeof(Derived)<<std::endl;
        printf("%p %p %p %p\n", &t, &t.a, &t.b, &t.c); // 先放基类，再放子类   
        return 0;
    }
#endif

#if 0
    class Test
    {
    public:
        // 虚函数指针的引入会影响内存的排布，编译器会优化，就可以把子类和父类放一起了，节省空间
        // 相对于直接往里面添一个指针而言，虚函数指针对内存的影响不大
        // 用多态时，编译器会减少虚函数指针对内存模型产生的影响，不同编译器可能内存模型就不一样了
        virtual void show()
        {}
    public:
        // 如果引入普通类型的指针，那还是分类排布
        int *p;
        int a;
        char b;
    };

    class Derived:public Test
    {
    public:
        char c;
    };

    int main()
    {
        Derived t;
        std::cout<<sizeof(Derived)<<std::endl;
        printf("%p %p %p %p %p\n", &t, &t.p, &t.a, &t.b, &t.c);
        return 0;
    }
#endif

#if 0
    class Test
    {
    public:
        virtual void show()
        {}
    public:
        int a;
        char b;
    };

    class Derived:public Test
    {
    public:
        // 自己定义一个虚函数
        // 一个类一张虚函数表，父类和子类不会共享一张虚函数表
        // 所以一个类不管有多少个虚函数，都只需要一个虚函数指针
        virtual void func(){}
        char c;
    };

    int main()
    {
        Derived t;
        std::cout<<sizeof(Derived)<<std::endl;
        printf("%p %p %p %p\n", &t, &t.a, &t.b, &t.c);
        return 0;
    }
#endif

#if 1
    class Test
    {
    public:
        virtual void show()
        {}
    public:
        int a;
    };

    // 虚继承，会多一个虚继承指针，也就多一张虚继承表
    // 很浪费空间，不是一个好方案
    class Derived:virtual public Test
    {
    public:
        char c;
    };

    int main()
    {
        Derived t;
        std::cout<<sizeof(Derived)<<std::endl;
        printf("%p %p %p\n", &t, &t.a, &t.c);
        return 0;
    }
#endif



    
